package org.chen.aao.ai.service.voice;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.google.cloud.speech.v1.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.chen.aao.ai.feign.OSSServiceClient;
import org.chen.aao.ai.feign.SubsServiceClient;
import org.chen.aao.ai.mapper.VoiceHistoryMapper;
import org.chen.aao.ai.service.IVoiceToTextService;
import org.chen.aao.common.ai.dto.VoiceHistoryResponse;
import org.chen.aao.common.ai.dto.VoiceToTextResponse;
import org.chen.aao.common.ai.entity.VoiceHistory;
import org.chen.aao.common.exception.AIServiceException;
import org.chen.aao.common.exception.QuotaExceededException;
import org.chen.aao.common.file.dto.FileUploadResponse;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class VoiceToTextServiceImpl implements IVoiceToTextService {

    private final RedisTemplate<String, Object> redisTemplate;
    private final OSSServiceClient ossService;
    private final SubsServiceClient subsServiceClient;
    private final SpeechClient speechClient;
    private final VoiceHistoryMapper voiceHistoryMapper;

    @Override
    // 语音转文字功能
    public VoiceToTextResponse voiceToText(MultipartFile audioFile, String language, Long userId) {
        String cacheKey = "voice:user:" + userId + ":daily";

        // 检查配额
        Integer dailyUsage = (Integer) redisTemplate.opsForValue().get(cacheKey);
        if (dailyUsage != null && dailyUsage >= getMaxDailyVoiceToText(userId)) {
            throw new QuotaExceededException("每日语音转文字次数已达上限");
        }

        try {
            // 上传音频到OSS
            FileUploadResponse fileUploadResponse = ossService.uploadAudioFile(audioFile);

            long fileSize = audioFile.getSize();

            // 准备语音识别请求
            byte[] audioBytes = audioFile.getBytes();
            RecognitionConfig config = RecognitionConfig.newBuilder()
                    .setEncoding(RecognitionConfig.AudioEncoding.WEBM_OPUS)
                    .setSampleRateHertz(48000)
                    .setLanguageCode(language != null ? language : "zh-CN")
                    .setEnableAutomaticPunctuation(true)
                    .build();

            RecognitionAudio audio = RecognitionAudio.newBuilder()
                    .setContent(com.google.protobuf.ByteString.copyFrom(audioBytes))
                    .build();

            // 执行识别
            RecognizeResponse response = speechClient.recognize(config, audio);
            StringBuilder transcript = new StringBuilder();

            for (SpeechRecognitionResult result : response.getResultsList()) {
                transcript.append(result.getAlternativesList().get(0).getTranscript());
            }

            // 更新配额
            redisTemplate.opsForValue().increment(cacheKey);
            redisTemplate.expire(cacheKey, 1, TimeUnit.DAYS);

            // 保存记录
            saveVoiceRecord(userId, language, fileUploadResponse.getUrl(), transcript.toString(), fileSize);

            return VoiceToTextResponse.builder()
                    .text(transcript.toString())
                    .audioUrl(fileUploadResponse.getUrl())
                    .confidence(response.getResultsList().get(0)
                            .getAlternativesList().get(0).getConfidence())
                    .build();

        } catch (Exception e) {
            log.error("Voice to text error for user {}: {}", userId, e.getMessage());
            throw new AIServiceException("语音识别服务暂时不可用");
        }
    }

    private int getMaxDailyVoiceToText(Long userId) {
        String subscriptionLevel = getSubscriptionLevel(userId);
        return switch (subscriptionLevel) {
            case "PREMIUM" -> 500;
            case "BASIC" -> 50;
            default -> 5;
        };
    }

    // 获取订阅等级
    private String getSubscriptionLevel(Long userId) {
        String cacheKey = "user:subscription:" + userId;
        String level = (String) redisTemplate.opsForValue().get(cacheKey);
        if (level == null) {
            level = subsServiceClient.getSubscriptionStatusById(userId).getData().getPlan();
            redisTemplate.opsForValue().set(cacheKey, level, 1, TimeUnit.HOURS);
        }
        return level;
    }

    // 保存语音记录
    private void saveVoiceRecord(Long userId, String language, String audioUrl, String transcript, Long fileSize) {
        // 保存语音记录
        VoiceHistory history = new VoiceHistory();
        history.setUserId(userId);
        history.setAudioUrl(audioUrl);
        history.setTranscript(transcript);
        history.setLanguage(language);
        history.setFileSize(fileSize);
        voiceHistoryMapper.insert(history);
        log.info("Voice record saved for user {}: {}", userId, audioUrl);
    }

    @Override
    public VoiceHistoryResponse getVoiceToTextHistory(Long userId, int page, int size) {
        Page<VoiceHistory> pageRequest = new Page<>(page, size);
        QueryWrapper<VoiceHistory> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", userId);

        queryWrapper.orderByDesc("created_at");

        Page<VoiceHistory> historyPage = voiceHistoryMapper.selectPage(pageRequest, queryWrapper);

        List<VoiceHistoryResponse.VoiceMessage> translations = historyPage.getRecords().stream()
                .map(history -> VoiceHistoryResponse.VoiceMessage.builder()
                        .id(String.valueOf(history.getId()))
                        .audioUrl(history.getAudioUrl())
                        .language(history.getLanguage())
                        .transcript(history.getTranscript())
                        .createdAt(history.getCreatedAt())
                        .build())
                .collect(Collectors.toList());

        return VoiceHistoryResponse.builder()
                .records(translations)
                .total((int) historyPage.getTotal())
                .hasMore(historyPage.hasNext())
                .build();
    }
}
